/**
 * @details: 
 * @brief: 
 * @version: 1.0
 * @author: liuri
 * @Date: 2021-10-05 05:40:46
 * @LastEditors: liuri
 * @LastEditTime: 2021-10-07 01:52:45
 * @FilePath: /TestProject/cPath/vtysh/cmd_matcher.c
 * @Copyright (C) 2021 liuri. All rights reserved.
 */

#include <string.h>
#include <stdlib.h>
#include "cmd.h"


#define CMD_ARGC_MAX   25


/**
 * @brief: cmd_matcher 初始化
 * @param matches 用 vector 存储，存储当前 index 下的匹配列表
 */
static
void cmd_matcher_init(struct cmd_matcher *matcher, vector vline, uint32_t index)
{
    memset(matcher, 0, sizeof(*matcher));
    matcher->vline = vline;
    matcher->index = index;
    matcher->match_type = no_match;
    matcher->match = vector_init(VECTOR_MIN_SIZE);
    matcher->word_index = 0;
}

/**
 * @brief: 检查输入命令匹配是否完成
 */
static 
int cmd_matcher_words_left(struct cmd_matcher *matcher)
{
    return matcher->word_index < vector_max(matcher->vline);
}

/**
 * @brief: 获取输入命令正在匹配的关键词
 */
static const 
char* cmd_matcher_get_word(struct cmd_matcher *matcher)
{
    cmd_matcher_words_left(matcher);
    //   assert(cmd_matcher_words_left(matcher));

    return vector_slot(matcher->vline, matcher->word_index);
}



/**
 * @brief: 将 token 插入 match 中，当获取帮助信息是可以直接使用
 * @param  match_type 匹配类型，也是优先级
 */
static void
cmd_matcher_record_match(struct cmd_matcher *matcher,
                         enum match_type match_type,
                         struct cmd_token *token)
{
    // 检查是否是当前最大匹配深度
    if (matcher->word_index != matcher->index)
        return;
    
    // 存储 token 向量
    vector_set(matcher->match, token);

    // 优先级比较
    if (match_type > matcher->match_type)
        matcher->match_type = match_type;
}

/**
 * @brief: 匹配 terminal 类型节点
 * @param 
 * @return 返回匹配错误
 */
static enum matcher_rv
cmd_matcher_match_terminal(struct cmd_matcher *matcher, struct cmd_token *token)
                        //    , int *argc, const char **argv)
{
    const char *word;
    enum match_type word_match;

    // assert(token->type == TOKEN_TERMINAL);

    // 匹配完成
    if (!cmd_matcher_words_left(matcher))
    {
        cmd_debug("Match over \n");
        return MATCHER_INCOMPLETE;
    }

    // 获取用户输入命令中正在匹配的关键词
    word = cmd_matcher_get_word(matcher);

    // 匹配
    word_match = cmd_word_match(token->terminal, token->cmd, word);
    if (word_match == no_match)  // 匹配失败
    {
        cmd_debug("Not match \n");
        return MATCHER_NO_MATCH;
    }
        
    // 将 token 插入 match 中，当获取帮助信息是可以直接使用
    cmd_matcher_record_match(matcher, word_match, token);

    // 准备匹配下一个
    matcher->word_index++;

    return MATCHER_OK;
}



/**
 * @brief: 匹配 multiple 向量，不能匹配多个
 */
static enum matcher_rv

 cmd_matcher_match_multiple(struct cmd_matcher *matcher,
                           struct cmd_token *token)
                        //    , int *argc, const char **argv)
{
    enum match_type multiple_match;
    unsigned int multiple_index;
    const char *word;
    // const char *arg = NULL;
    struct cmd_token *word_token;
    enum match_type word_match;

    multiple_match = no_match;

    // 检查是否匹配结束
    if (!cmd_matcher_words_left(matcher))
        return MATCHER_INCOMPLETE;

    // 获取用于输入的匹配关键字
    word = cmd_matcher_get_word(matcher);

    // 遍历多选数组, 获取所有匹配结果
    for (multiple_index = 0; multiple_index < vector_max(token->multiple); multiple_index++)
    {
        // 获取多选数组
        word_token = vector_slot(token->multiple, multiple_index);

        word_match = cmd_word_match(word_token->terminal, word_token->cmd, word);
        if (word_match == no_match)
            continue;

        // 将 word_token 存入matcher 中
        cmd_matcher_record_match(matcher, word_match, word_token);
    }

    matcher->word_index++;

    if (multiple_match == no_match)
        return MATCHER_NO_MATCH;

    // 匹配成功
    return MATCHER_OK;
}


static enum matcher_rv
cmd_matcher_read_keywords(struct cmd_matcher *matcher,
                          struct cmd_token *token,
                          vector args_vector)
{
    unsigned int i;
    unsigned long keyword_mask;
    unsigned int keyword_found;
    enum match_type keyword_match;
    enum match_type word_match;
    vector keyword_vector;
    struct cmd_token *word_token;
    const char *word;
    // int keyword_argc;
    const char **keyword_argv;
    enum matcher_rv rv = MATCHER_NO_MATCH;

    keyword_mask = 0;
    while (1)
    {
        if (!cmd_matcher_words_left(matcher))
            return MATCHER_OK;

        word = cmd_matcher_get_word(matcher);

        keyword_found = -1;
        keyword_match = no_match;

        // 遍历 keyword 数组
        for (i = 0; i < vector_max(token->keyword); i++)
        {
            if (keyword_mask & (1 << i))
                continue;

            // 获取keyword 向量分量
            keyword_vector = vector_slot(token->keyword, i);

            word_token = vector_slot(keyword_vector, 0);

            word_match = cmd_word_match(word_token->terminal, word_token->cmd, word);
            if (word_match == no_match)
                continue;

            // word_token 放入 matcher 中
            cmd_matcher_record_match(matcher, word_match, word_token);

            // 得到匹配成功的下标
            if (word_match > keyword_match)
            {
                keyword_match = word_match;
                keyword_found = i;
            }
            else if (word_match == keyword_match)
            {
                if (matcher->word_index != matcher->index || args_vector)
                    return MATCHER_AMBIGUOUS;
            }
        }

        if (keyword_found == (unsigned int)-1)
            return MATCHER_NO_MATCH;

        matcher->word_index++;

        if (matcher->word_index > matcher->index)
            return MATCHER_OK;

        keyword_mask |= (1 << keyword_found);

        if (args_vector)
        {
            // keyword_argc = 0;
            keyword_argv = malloc((CMD_ARGC_MAX + 1) * sizeof(char*));
            /* We use -1 as a marker for unused fields as NULL might be a valid value */
            for (i = 0; i < CMD_ARGC_MAX + 1; i++)
            keyword_argv[i] = (void*)-1;
            vector_set_index(args_vector, keyword_found, keyword_argv);
        }
        else
        {
            keyword_argv = NULL;
        }

        keyword_vector = vector_slot(token->keyword, keyword_found);
        /* the keyword itself is at 0. We are only interested in the arguments,
        * so start counting at 1. */
        for (i = 1; i < vector_max(keyword_vector); i++)
        {
            word_token = vector_slot(keyword_vector, i);

            switch (word_token->type)
            {
                case TOKEN_TERMINAL:
                rv = cmd_matcher_match_terminal(matcher, word_token);
                break;
                case TOKEN_MULTIPLE:
                rv = cmd_matcher_match_multiple(matcher, word_token);
                break;
                case TOKEN_KEYWORD:
                // assert(!"Keywords should never be nested.");
                break;
            }

            if (MATCHER_ERROR(rv))
            return rv;

            if (matcher->word_index > matcher->index)
                return MATCHER_OK;
        }
    }
    /* not reached */
}



/**
 * @brief: 匹配 keyword 向量
 */
static enum matcher_rv
cmd_matcher_match_keyword(struct cmd_matcher *matcher, struct cmd_token *token)
{
    vector keyword_args_vector;
    enum matcher_rv reader_rv;
   
    reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);

    if (!MATCHER_ERROR(reader_rv))
        return reader_rv;

    return reader_rv;
}





/**
 * @brief: 递进获取所有匹配的命令
 * @param deep 当前匹配的字符串深度
 */
int cmd_vector_filter(struct cmd_matcher *matcher, vector commands, vector vline, unsigned int deep)
{
    uint32_t i;
    struct cmd_element *cmd_element;
    enum matcher_rv matcher_rv;
    // 轮询当前根节点下的所有命令
    for (i = 0; i < vector_max (commands); i++)
    {
        cmd_element = vector_slot (commands, i);
        cmd_element_dump(cmd_element);
        if(NULL == cmd_element)
            continue;

        matcher_rv = cmd_matcher_match(matcher, cmd_element->tokens, vline, deep);
        if (MATCHER_ERROR(matcher_rv))
        {
            return CMD_ERR_AMBIGUOUS;
        }   
    }
    
    return CMD_SUCCESS;
}
